package comm;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;

// 通过socket接收监视服务消息，由监视服务主动通信
class ServerForWitness extends Thread {
	private boolean isEnd;
	private ServerSocket server;
	private Socket socket;
	private InputStream in;
	private OutputStream out;
	private final int socketReadTimeLimit = 100; // 读socket超时（ms）
	private Log log = new Log();

	// 消息应答相关
	private EStateOfServerForWitness state = EStateOfServerForWitness.WaitingReq; // 处理状态
	private int commandReq;
	private int commandAck;
	private boolean processStatus; // true表待处理，false表已处理

	ServerForWitness() {
		this.setName(this.getClass().getName());
		this.isEnd = false;
	}

	Log getLog() {
		return this.log;
	}

	boolean isEnd() { // 线程是否已被终止
		return this.isEnd;
	}

	// 终止线程
	// // 不涉及数据安全，可以直接硬关闭线程
	void end() {
		isEnd = true;
		try {
			if (null != this.server) {
				this.server.close();
				this.server = null;
			}
			if (null != this.socket) {
				this.socket.close();
				this.socket = null;
			}
		} catch (IOException e) {
			Log.println(e.toString());
		}
	}

	// 消息应答相关
	boolean getProcessStatus() { // mainloop调用
		return this.processStatus;
	}

	int getReq() { // mainloop调用
		return this.commandReq;
	}

	void setAck(int aCommand) { // mainloop调用
		this.commandAck = aCommand;
		this.processStatus = false;
	}

	private void setReq(int aCommand) {
		this.commandReq = aCommand;
		this.processStatus = true;
	}

	private int getAck() {
		return this.commandAck;
	}

	// 监视服务应答消息处理线程
	// // 处理流程：接受监视服务连接，解析消息，等待应答，发送应答，关闭连接
	public void run() {
		try {
			this.log.print(Log.info, "监视服务监听线程启动:");
			// 初始化
			int num = PropertyManager.getInt(
					"PortForWitnessServer", 12345);
			if(num < 0) num = 12345;
			server = new ServerSocket(num);

			num = PropertyManager.getInt(
							"TimeOutTimeForWitness", 60);
			if(num < 0) num =60;
			TimeOutTimer timeOutTimer = new TimeOutTimer(
					1000 * (long) num); // 数据等待超时定时器
			DataForWitness dataForWitness = DataForWitness.getData();

			byte[] buffer = new byte[100]; // 接收缓存，仅用到16字节
			int bufferSize = 0;

			while (isEnd == false) {
				socket = server.accept(); // 阻塞操作
				// Log.println("接受新连接");
				in = socket.getInputStream();
				out = socket.getOutputStream();

				// 初始化socket读数据超时时间
				try {
					socket.setSoTimeout(socketReadTimeLimit);
				} catch (SocketException e) {
					// e.printStackTrace();
					log.print(Log.error, e.toString());
					isEnd = true;
				}

				// 初始化新连接
				this.state = EStateOfServerForWitness.WaitingReq; // 等待请求
				bufferSize = 0;
				timeOutTimer.resetTimer();
				// 新连接处理
				while (isEnd == false) {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						Log.println(e.toString());
					}

					if (true == timeOutTimer.getTimeOutState()) {
						// 超时退出
						// // 可能有两种超时（监视服务导致到超时或内部程序导致到超时）
						// // 超时时间应大于监视服务超时关闭连接时间
						break;
					}

					// 为方便直接break到while外边，此处用if-else不用switch
					if (true == this.state
							.equals(EStateOfServerForWitness.WaitingReq)) {
						// 等待数据状态（等待监视服务数据）
						try {
							int readNum = in.read(buffer, bufferSize,
									buffer.length - bufferSize);
							if (readNum > 0) {
								timeOutTimer.resetTimer();
								// Log.println("接收数据...");
								bufferSize += readNum;
								/*
								 * Log.println("收到：" + readNum + ":"); for (int
								 * i = 0; i < readNum; i++) {
								 * System.out.print(buffer[i] & 0xff);
								 * System.out.print(":"); }
								 */

								if (bufferSize >= 16) {
									final int newCommandID = dataForWitness
											.decode(buffer, 0);
									final DataForWitness.ECommand newCommand = DataForWitness.ECommand
											.getCommand(newCommandID);
									if (newCommandID > 0 && null != newCommand) {
										// Log.println("收到指令:" + newCommandID +
										// ":" + newCommand);
										this.setReq(newCommandID);
										this.state = EStateOfServerForWitness.WaitingAck;
									} else {
										Log.println("无效指令,ID:" + newCommandID);
										log.print(Log.warn, "来自监视服务无效指令,ID:" + newCommandID);
										break;
									}

								}
							}
						} catch (SocketTimeoutException e) {
							// System.out.println("等待数据超时");
						}
					} else if (true == this.state
							.equals(EStateOfServerForWitness.WaitingAck)) {
						// 等待处理状态
						if (false == this.getProcessStatus()) {
							// 处理完成
							out.write(dataForWitness.encode(this.getAck()));
							this.state = EStateOfServerForWitness.Finished;
							break;
						}
					} else {
						// 其他非正常状态
						break;
					}
				}

				out.close();
				in.close();
				socket.close();
				socket = null;
				// Log.println("监视服务连接关闭");
			}
		} catch (Exception e) {
			Log.println(e);
			log.print(Log.error, e.toString());
		} finally {
			this.end();
			this.log.print(Log.info, "监视服务监听线程终止");
		}
	}

	// 与监视服务问答响应状态
	private enum EStateOfServerForWitness {
		WaitingReq, WaitingAck, Finished
	}

}

// 监视服务与通信服务问答数据
class DataForWitness {
	final static int DataSize = 16;

	private DataForWitness() {
	}

	static DataForWitness getData() {
		DataForWitness dataForWitness = new DataForWitness();
		return dataForWitness;
	}

	// 编码
	byte[] encode(int aCommand) {
		byte[] buffer = new byte[16];
		buffer[0] = (byte) 0x0a;
		buffer[1] = (byte) 0xfa;
		buffer[14] = (byte) 0xa0;
		buffer[15] = (byte) 0xaf;

		buffer[2] = (byte) (aCommand & 0xff);
		buffer[3] = (byte) (aCommand >>> 8 & 0xff);
		buffer[4] = (byte) (aCommand >>> 16 & 0xff);
		buffer[5] = (byte) (aCommand >>> 24 & 0xff);

		return buffer;
	}

	// 解码
	int decode(byte[] aBuffer, int aOffset) {
		if (aBuffer.length - aOffset >= 16) {
			if (aBuffer[aOffset + 0] == (byte) 0x0a
					&& aBuffer[aOffset + 1] == (byte) 0xfa
					&& aBuffer[aOffset + 14] == (byte) 0xa0
					&& aBuffer[aOffset + 15] == (byte) 0xaf) {
				// 不能直接将byte赋值给int，可能会有负数问题
				int num = ((int) aBuffer[aOffset + 5]) & 0xff;
				num = num << 8;
				num += ((int) aBuffer[aOffset + 4]) & 0xff;
				num = num << 8;
				num += ((int) aBuffer[aOffset + 3]) & 0xff;
				num = num << 8;
				num += ((int) aBuffer[aOffset + 2]) & 0xff;
				return num;
			}
		}
		return -1;
	}

	// 指令（监视服务发，通信服务收）
	// // 有效指令值为正数
	enum ECommand {
		TimeSeconds(1);

		final int commandID;

		private ECommand(final int aCommandID) {
			this.commandID = aCommandID;
		}

		static ECommand getCommand(final int aCommandID) {
			switch (aCommandID) {
			case 1:
				return ECommand.TimeSeconds;

			default:
				return null;
			}
		}

		int getCommandID() {
			return this.commandID;
		}
	}
}

// ** 待改进


// ？？可改为带参数问答（可检测多种情况，并可查询数据库状态，可扩展性好）

// ？？可增加需要检查对方IP（仅供监视服务调用，可提高安全性）


// ** 说明

// 监视协议：单次连接累计收到16字节数据，解析，解析成功提交处理，等待处理完成则回复16字节应答数据，关闭连接。解析失败关闭连接
